/*
* Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: Camerality OpenGL rendering class.
*              Rendering copied from Forum Nokia "Simple Cube" example.
*
*/

// INCLUDE FILES
#include <math.h>
#include <w32std.h> // RWindow
#include <alfcompositionutility.h> // CAlfCompositionSource

#include "cameralitytraces.h"
#include "cameralityglrenderer.h"

// MACROS
#define FRUSTUM_LEFT   -1.f     //left vertical clipping plane
#define FRUSTUM_RIGHT   1.f     //right vertical clipping plane
#define FRUSTUM_BOTTOM -1.f     //bottom horizontal clipping plane
#define FRUSTUM_TOP     1.f     //top horizontal clipping plane
#define FRUSTUM_NEAR    3.f     //near depth clipping plane
#define FRUSTUM_FAR  1000.f     //far depth clipping plane

// CONSTANTS
/**
* Vertice coordinates for the cube.
*/
static const GLbyte vertices[8 * 3] =
    {
    -1,  1,  1,
    1,  1,  1,
    1, -1,  1,
    -1, -1,  1,

    -1,  1, -1,
    1,  1, -1,
    1, -1, -1,
    -1, -1, -1
    };

/**
*  Colors for vertices (Red, Green, Blue, Alpha).
*/
static const GLubyte colors[8 * 4] =
    {
    0  ,255,  0,255,
    0  ,  0,255,255,
    0  ,255,  0,255,
    255,  0,  0,255,

    0  ,  0,255,255,
    255,  0,  0,255,
    0  ,  0,255,255,
    0  ,255,  0,255
    };


/**
* Indices for drawing the triangles.
* The color of the triangle is determined by
* the color of the last vertex of the triangle.
*/
static const GLubyte triangles[12 * 3] =
    {
    /* front */
    1,0,3,
    1,3,2,

    /* right */
    2,6,5,
    2,5,1,

    /* back */
    7,4,5,
    7,5,6,

    /* left */
    0,4,7,
    0,7,3,

    /* top */
    5,4,0,
    5,0,1,

    /* bottom */
    3,7,6,
    3,6,2
    };


// ================= MEMBER FUNCTIONS =======================

/*!
* Two-phased constructor.
* @return Renderer instance.
*/
CCameralityGLRenderer* CCameralityGLRenderer::NewL()
    {
    CCameralityGLRenderer* self = new (ELeave) CCameralityGLRenderer();
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }

/*!
* Constructor.
*/
CCameralityGLRenderer::CCameralityGLRenderer()
    : iState(EIdle),
      iScreenWidth(0),
      iScreenHeight(0),
      iFrame(0)
    {
    }


/*!
* Second phase constructor that can call methods that may leave.
*/
void CCameralityGLRenderer::ConstructL()
    {
    // Create an active object for animating the scene
    iPeriodic = CPeriodic::NewL(CActive::EPriorityIdle);
    }

/*!
* Destructor.
* Releases any used resources.
*/
CCameralityGLRenderer::~CCameralityGLRenderer()
    {
    TRACE_ENTER();
    Stop();
    delete iCompSource;
    delete iPeriodic;
    TRACE_EXIT();
    }

/*!
* Initialize surface on a window and start rendering.
* @param aWindow Native window to start rendering on.
*/
void CCameralityGLRenderer::Start(RWindow& aWindow)
    {
    TRACE_ENTER();
    if (iState == EIdle)
        {
        iWindow = &aWindow;
        SetupScene();

        // Start updating the scene at fixed interval
        iPeriodic->Start(50*1000, 10*1000,
                         TCallBack(CCameralityGLRenderer::RenderCallback, this));
        }
    TRACE_EXIT();
    }

/*!
* Resize the rendering area.
*/
void CCameralityGLRenderer::Resize(const TSize& aSize)
    {
    TRACE_VISIT();
    iScreenWidth  = aSize.iWidth;
    iScreenHeight = aSize.iHeight;
    if (iEglDisplay)
        {
        glViewport(0, 0, iScreenWidth, iScreenHeight);
        }
    }

/*!
* Stop OpenGL rendering and release any resources needed for it.
*/
void CCameralityGLRenderer::Stop()
    {
    TRACE_ENTER();
    if (iState == ERendering)
        {
        if (iPeriodic)
            {
            iPeriodic->Cancel();
            }

        // Deinit EGL
        eglMakeCurrent(iEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
        eglDestroyContext(iEglDisplay, iEglContext);
        eglDestroySurface(iEglDisplay, iEglSurface);
        eglTerminate(iEglDisplay);
        eglReleaseThread();
        iEglDisplay = 0;
        iEglSurface = 0;
        iEglContext = 0;

        // Delete the composition source as it's specific to a window.
        delete iCompSource;
        iCompSource = NULL;

        iWindow = NULL;
        iState = EIdle;
        }
    TRACE_EXIT();
    }

/*!
* Return our EGL display instance.
*/
EGLDisplay CCameralityGLRenderer::Display() const
    {
    return iEglDisplay;
    }

/*!
* Called by the CPeriodic in order to draw the graphics.
*/
TInt CCameralityGLRenderer::RenderCallback(TAny* aInstance)
    {
    CCameralityGLRenderer* instance = (CCameralityGLRenderer*) aInstance;
    return instance->RenderScene();
    }

/*!
* Render contents of our scene.
*/
TInt CCameralityGLRenderer::RenderScene()
    {
    if (iState == EIdle) {
        SetupScene();
    }

    static float rx = 0.0f;
    static float gx = 0.0f;
    rx += 0.1f;
    gx += 0.1f;

#ifdef __WINSCW__
    glClearColor(0, 0, 0, 0.1);
#else
    glClearColor(0, 0, 0, 0);
#endif
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    const GLint cameraDistance = 100;

    iFrame += 5;

    // Animate and draw box
    glLoadIdentity();
    glTranslatex( 0 , 0 , -cameraDistance << 16 );
    glRotatex(iFrame << 16, 1 << 16,    0   ,    0    );
    glRotatex(iFrame << 15,    0   , 1 << 16,    0    );
    glRotatex(iFrame << 14,    0   ,    0   , 1 << 16 );
    DrawBox(8, 8, 8);

    glFinish();
    eglWaitGL();
    eglWaitClient();
    eglWaitNative(EGL_CORE_NATIVE_ENGINE);

    // Call eglSwapBuffers, which blit the graphics to the window
    eglSwapBuffers(iEglDisplay, iEglSurface);

    return 0;
    }

/*!
* Helper method to render a given size box to the surface.
*/
void CCameralityGLRenderer::DrawBox(GLfixed aSizeX, GLfixed  aSizeY, GLfixed aSizeZ)
    {
    glScalex(aSizeX << 16, aSizeY << 16, aSizeZ << 16);
    glDrawElements(GL_TRIANGLES, 12 * 3, GL_UNSIGNED_BYTE, triangles);
    }

/*!
* Initialize the surface and setup other scene details.
*/
void CCameralityGLRenderer::SetupScene()
    {
    TRACE_ENTER();
    if (iState == EIdle)
        {
        // Initialize default display
        iEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        eglInitialize(iEglDisplay, NULL, NULL);

        // Set OpenGL ES as the current rendering API for the thread
        eglBindAPI(EGL_OPENGL_ES_API);

        // Get bit depth that matches the current window display mode.
        // Especially important in non ScreenPlay environment.
        TDisplayMode mode(iWindow->DisplayMode());
        TInt bits(TDisplayModeUtils::NumDisplayModeBitsPerPixel(mode));

        // List of attributes to pass to eglChooseConfig.
        const EGLint    KColorRGB565AttribList[] =
            {
            EGL_SURFACE_TYPE,     EGL_WINDOW_BIT,
#ifndef __WINSCW__
            EGL_RENDERABLE_TYPE,  EGL_OPENGL_ES2_BIT,
#else
            EGL_RENDERABLE_TYPE,  EGL_OPENGL_ES_BIT,
#endif
            EGL_BUFFER_SIZE, bits,
            EGL_DEPTH_SIZE, 16,
            EGL_NONE
            };

        EGLint numConfigs(0);

        // Get the config to use.
        eglChooseConfig(iEglDisplay, KColorRGB565AttribList, &iConfig, 1, &numConfigs);

        if (numConfigs == 0)
            {
            TRACE("No matching configs found");
            _LIT(KCreateWindowSurfaceFailed, "eglChooseConfig failed");
            User::Panic(KCreateWindowSurfaceFailed, 0);
            }


        iEglSurface = eglCreateWindowSurface(iEglDisplay, iConfig, iWindow, NULL);
        if (iEglSurface == 0)
            {
            _LIT(KCreateWindowSurfaceFailed, "eglCreateWindowSurface failed");
            User::Panic(KCreateWindowSurfaceFailed, 0);
            }

        iEglContext = eglCreateContext(iEglDisplay, iConfig, EGL_NO_CONTEXT, NULL);
        if (iEglContext == 0)
            {
            _LIT(KCreateContextFailed, "eglCreateContext failed");
            User::Panic(KCreateContextFailed, 0);
            }

        // Make the context current. Binds context to the current rendering thread and surface.
        if (eglMakeCurrent(iEglDisplay, iEglSurface, iEglSurface, iEglContext) == EGL_FALSE)
            {
            _LIT(KMakeCurrentFailed, "eglMakeCurrent failed");
            User::Panic(KMakeCurrentFailed, 0);
            }

        // Recalculate the view frustrum
        glMatrixMode( GL_PROJECTION );
        glLoadIdentity();
        GLfloat aspectRatio = (GLfloat)(iScreenWidth) / (GLfloat)(iScreenHeight);
        glFrustumf(FRUSTUM_LEFT * aspectRatio,
                   FRUSTUM_RIGHT * aspectRatio,
                   FRUSTUM_BOTTOM,
                   FRUSTUM_TOP,
                   FRUSTUM_NEAR,
                   FRUSTUM_FAR );
        glMatrixMode(GL_MODELVIEW);

        // Set the screen background color.
        glClearColor(0.f, 0.f, 0.f, 0.f);

        // Enable back face culling.
        glEnable(GL_CULL_FACE);

        // Enable vertex arrays.
        glEnableClientState(GL_VERTEX_ARRAY);
        // Set array pointers.
        glVertexPointer(3, GL_BYTE, 0, vertices);

        // Enable color arrays.
        glEnableClientState(GL_COLOR_ARRAY);
        // Set color pointers.
        glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
       
        // Do not use perspective correction
        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);

        // Enable transparency for the window using CAlfCompositionSource.
        TInt error(KErrNone);
        if (!iCompSource)
            {
            TRAP(error, iCompSource = CAlfCompositionSource::NewL(*iWindow));
            }
        if (!error)
            {
            error = iCompSource->EnableAlpha(ETrue);
            TRACE_ARG("[Camerality] Status enabling alpha: %d", error);
            }
        else
            {
            TRACE_ARG("[Camerality] Could not create composition source, error: %d", error);
            }
        iState = ERendering;
        }
    TRACE_EXIT();
    }


// End of file
